[10/10/99]Ŀ
----     <-- Safedisc Cracking Tutorial by Tola[AmoK] -->     ---Ĵ


 Content - English Edition
            (if you encounter any grammatical mistakes -
                                              I'm German, what can I do ;)

    I.  Introduction
        1. About this tutorial
        2. About Safedisc
        3. Recognizing safedisc protected programs
        4. Required tools / knowledge
            a) Assembly language
            b) PE file format
            c) SoftIce for Win9x
            d) FrogSice 0.31
            e) ProcDump 1.5
            f) ADump
            g) Included tools
            h) Time and patience
    II. Cracking the program
        1. Preparations
        2. Dumping the executable
        3. The import table
            a) What safedisc did to it
            b) Repairing the import table
            c) Compatibility issues
    III.Final words / thanks & greetings / contacting the author

 Tutorial

    I. Introduction

       1. About this tutorial

          This tutorial will cover cracking any programs
          (most of them are games) that are protected by
          C-Dilla's Safedisc. I will refer to the new version
          of safedisc that is being used for some weeks now.
          Because some changes have been made to make this
          protection harder to crack the generic safedisc
          crack by Laxity will not work and you will not
          be able to create a working exe file by using
          the methods presenteed in Black Check's tutorial.
          Although this is my first tutorial ever I tried to
          explain everything as comprehensibly and thoroughly
          as possible. Unfortunately I have to take some
          knowledge for granted as otherwise the explanations
          wouldn't be within the realms of possibility of
          this tutorial (this especially refers to the structure
          of the pe file format or the import table).

       2. About Safedisc.

          Safedisc is a commercial copy protection for CD ROMs
          that almost every new game uses. The following key
          features are meant to provide a strong protection
          against copying the protected cds:
          - A digital signature is written to every CD which
            cannot be copied using a common CD writer. At
            the beginning of the CD (about the first two
            minutes) you can find a lot of read errors. This
            shall prevent you from making a 1:1 copy of the cd.
            Using special software (e.g. Nero oder CDRWin)
            you can still make a copy but the game will not
            work nevertheless.
          --> You'll need the original CD to crack the program.
          - The file that has to be executed is, to some extent,
            encrypted. A loader that reads and verifies the
            signature on the CD will decrypt that file and
            run it thereafter. Encrypted files can be recognized
            by the extension '.IDC'.
          - Anti debugging tricks (jumping into other opcodes
            to prevent you from disassembling the file and
            multiple softice detection methods) are meant to
            prevent reverse engineering of the loader and the
            protected exe file.

       3. Recognizing safedisc protected programs

          Recognizing safedisc protedcted program is quite
          simple:
          - You will find the following files on the protected
            CD or in the installation directory:
            CLCD16.DLL, CLCD32.DLL, CLOKSPL.EXE, DPLAYERX.DLL
            and one or more files having a numerical name and
            the extension '.16' or '.256' (these are the
            bitmaps that are displayed during the loading
            process) and a file with the extension '.TMP'
            (usually 00000001.TMP).
          - The exe file that runs the game is about 300 KB
            in size and there is another file with the same
            name and the extension '.ICD'.
          - You may eventually not be able to copy the CD
            due to the huge amount of reading errors.

       4. Required tools / knowledge

          In order to create a working exe file you wille need
          some important tools I'm going to present to you
          now. You should find all of them on the net. If you
          don't know where to search check out Programmer's
          Tools (w3.to/protools) as a starting point.
        
       a) Assembly language

          You will need at least basic knowledge as assembly
          in order to understand this tutorial.

       b) PE file format
          
          This topicis has a great complexity in some parts
          so I can't explain everything in this tutorial.
          But don't despair, there are a lot of good sources
          of information on the net.

       c) SoftIce for Win9x

          The tool of the trade, do I have to say more?
          I recommend you download the latest verion
          (4.0 at the moment), but an older version should
          do fine, too.

       d) FrogSice 0.31

          This program detects a variety of anti softice
          tricks and tries to conceal softice if possible.
          Without this tool the cracking process would be
          a lot harder! You can get it at
          www.thepentagon.com/frog_s_print

       e) ProcDump 1.5

          Writing exe files from RAM to a file (dumping them)
          is done by this program. Moreover it's possible
          to view and edit certain important details of
          pe files. The newest version should be available
          at procdump32.cjb.net, check it out.

       f) ADump

          ADump is a so-called dumper for SoftIce. It re-
          serves a memory range we can access when using
          SoftIce to save some data we want to write to
          a file thereafter.

       g) Inlcuded tools

          The archive this tutorial came in contains two
          small programs (GetAddr.exe and RepImp.exe) you
          will need to repair the import table (more on
          this later). You are free to examine them
          thoroughly, anyone who wants the source code
          (pretty uncommented ;) can email me. I'll explain
          their usage in II.2.c).
       
       h) Time and patience

          You'll surely need that when it comes to repairing
          the import table.

    II. Cracking the program

        Some thing to keep in mind:
        - In this tutorials you will often find memory
          addresses and other program dependent details.
          These refer to the game I cracked when writing
          this tutorial and will differ in the program
          you may want to crack.
        - It's a good idea to write down some offsets
          and values to have them handy during the
          cracking progress. To simplify this I created
          a template (template.rtf, included in archive)
          you may use.
        Got it? Let's go!

       1. Preparations

          Before we can start cracking the hell out of our
          target we must collect some important information
          about it.
          - The entry point
            The execution of the program starts at the
            address that is specified by the program entry
            point. We need to obtain the entry point of
            the file XXX.ICD (xxx is the name of the file).
            You can easily do this using the PE editor
            in ProcDump. In my case ("Rogue Spear") it
            it 0029D150.
            ! --> The entry point is a relative virtual
                  address (called RVA). You can find dozens
                  of them in a single pe file. A RVA
                  describes a memory address of which the
                  base address is not known to us. To get
                  the linear memory address you just add
                  the base address to the RVA. In this case
                  the base address is the "Image Base" of
                  the exe file. We can find it in the pe
                  editor, too.
            The image base is 0040000 so program execution
            starts at the linear address 0069D150.
          - The .rdata section
            We will have to deal with this part of the exe-
            cutable in II.3. But for now we can already
            write down the section's "virtual offset"
            (it's an RVA again) and "virtual size" (you can
            find this information by clicking "section"
            in the pe editor). This section usually contains
            the executables import table. Please not that
            it need not be named .rdata! If there is no
            section called .rdata you will have to look
            up the import table's RVA in the pe editor
            and find out what section it belongs to
            (I never encountered such a file, though).
            The operating system uses the import table
            to find out which functions of which DLL are
            used by the program and provides the addresses
            of these functions. If you want a better ex-
            planation go and grab a good pe file format
            documentation.
          - ADump's starting address
            Before we start cracking we have to launch
            ADump. The command 'r' makes the program
            display its internal variables including
            the starting offset of the allocated
            memory region (STARTOFFS). Write down this
            value. If your computer should crash or
            you have to restart ADump you will have to
            write down the new address.

       2. Dumping the executable

          Enough of that jabbering, let's get it on.
          The first thing we'll do is dumping the EXE
          file to disk (i.e. reading the data from
          RAM and writing it to disk). Why, you ask
          me. Well, every time you start the program
          the loader will load (what else should a
          loader do ;) the encrypted executable,
          verifies the CD, decrypts the file and runs
          it. All we have to do is wait until the program
          starts and then write it do disk.
          But please note:
          In order to create an "exact" copy of the file
          we have to dump it before any internal data
          have been changed (e.g. by reading configuration
          data). This may (but not necessarily) impair
          the file. So we have to stop the program when
          it reaches its entry point (i.e. just when
          the decrypted program is run) so we have to
          set a break point on the program entry point.
          This is how we'll do it:
          Start the program. As soon as the splash screen
          disappears you may get into SoftIce and set
          a break point on the program's entry point.
          Softice should break just after you go back
          to windows. Restart the program if it doesn't.
          As soon as the break point is triggered set
          EIP to the ADump starting address and assemble
          the following instruction using the 'a' command:

          "jmp eip"

          On the one hand this creates a continous loop,
          on the other hand we won't change the executable.
          Exit SoftIce, run ProcDump and dump the file
          XXX.ICD to a file (rightclick the file and
          select 'Dump (full)').
          Fine, now we've got a executable, aren't we
          finished yet? Well, I have to disappoint you.
          Unfortunately this file will not work (of
          course you may try it if you want). If we
          try to run it all we'll get is a fancy error
          message. Now there's a problem, but none we
          can't deal with.

       3. The import table

       a) What Safedisc did to it

          The problem we'll have to deal with is the
          import table I've already mentioned before.
          In every safedisc protected executable the
          import table is messed up. All Kernel32
          and User32 imports point to a safedisc
          function (i.e. if the GetVersion function
          should be called the safedisc function
          is called instead which will obtain the
          address of GetVersion and call it).
          This method will not work in the dumped
          EXE as the DLL that does this is not
          loaded. Let's have a closer look at the code
          at the program entry point:

          017F:0069D150  PUSH      EBP                ;entry point
          017F:0069D151  MOV       EBP,ESP
          017F:0069D153  PUSH      FF
          017F:0069D155  PUSH      0076AE90
          017F:0069D15A  PUSH      006A0200
          017F:0069D15F  MOV       EAX,FS:[00000000]
          017F:0069D165  PUSH      EAX
          017F:0069D166  MOV       FS:[00000000],ESP
          017F:0069D16D  SUB       ESP,58
          017F:0069D170  PUSH      EBX
          017F:0069D171  PUSH      ESI
          017F:0069D172  PUSH      EDI
          017F:0069D173  MOV       [EBP-18],ESP
          017F:0069D176  CALL      [0075F1C8]         ;call to safedisc
                                                      ;function

          The last call in this listing should be a call
          to the GetVersion function. The address 0075F1C8
          is part of the import table. Originally the
          address of the GetVersion function would be
          located at 0075F1C8 (Windows inserts it there
          before running the program).
          Okay, let's trace into the call (F8). We will run
          into this:

          017F:00AE72A0  PUSHAD
          017F:00AE72A1  PUSH      00000050
          017F:00AE72A6  PUSH      00000000
          017F:00AE72AB  CALL      [00AE72C1]
          017F:00AE72B1  ADD       ESP,08
          017F:00AE72B4  POPAD
          017F:00AE72B5  JMP       [00AE72BB]

          As you can see another function is called
          from here (please do not trace into this
          one yet). After this function has been
          executed the actual address of GetVersion
          is written in 00AE72BB. The function also
          returns to values:
          After calling this function ECX contains
          the address of the first function of this
          kind. If you add EDX to ECX you will get
          00AE72A0 which is the address of the above
          function. Interesting, isn't it? If you
          continue to trace through the file you
          will notice that there are a lot of these
          functions that hand over different parameters
          to the safedisc function (you can get the
          address of the safedisc function by entering
          "d 00AE72C1" in Softice, in my case it's
          00A800C0). Here's the meaning of the
          parameters:

          017F:00AE72A1  PUSH      00000000   ;number of function
          017F:00AE72A6  PUSH      00000000   ;number of DLL
          017F:00AE72AB  CALL      00A800C0

          The second parameter represents the DLL the
          function is located in (0 = Kernel32, 1= User32).
          We can use the way this function works to our
          advantage. As seen above, the doubleword at
          address 0075F1C8 points to the routine we just
          discussed. If this reference is replaced by
          the correct linear address of the importet
          function the program should actually work!
          Unfortunately we do not know the address of
          the function. It must be calculated somewhere
          in that safedisc function, though. So let's
          have a closer look and trace through that
          function at address 00A800C0 (don't let those
          jumps irritate you) until we reach the following
          code:
        
          017F:00A8064B  MOV       ECX,[EBP-08]
          017F:00A8064E  PUSH      ECX
          017F:00A8064F  MOV       EDX,[EBP+08]
          017F:00A80652  PUSH      EDX
          017F:00A80653  CALL      00A7EE10
          017F:00A80658  ADD       ESP,08
          017F:00A8065B  MOV       [EBP-04],EAX
          017F:00A8065E  JMP       00A80669                                                                                  (JUMP )

          The instruction at address 00A8065B is quite
          interesting for our purposes. If you check
          EAX at that point you will notice that it
          points directly to the imported function!
          Excellent, now we just have to make sure
          this address does not get lost as the
          content of EAX will change later in this
          function. So keep on tracing through it
          until you reach the end of the function:

          017F:00A80A96  POP       EDI
          017F:00A80A97  POP       ESI
          017F:00A80A98  POP       EBX
          017F:00A80A99  MOV       ESP,EBP
          017F:00A80A9B  POP       EBP
          017F:00A80A9C  RET
          017F:00A80A9D  INT       3
          017F:00A80A9E  INT       3
          017F:00A80A9F  INT       3

          As you can see there are three unused bytes
          at the end of that function. That's just what
          we need. Replace the instruction at address
          00A80A9B by these:

          017F:00A80A9B  MOV       EAX,[EBP-4]
          017F:00A80A9E  POP       EBP
          017F:00A80A9F  RET

          Now this function will return the correct
          address of the importet function in EAX.
          Note --> The old version of safedisc returned
                   the address of the importet function
                   in ECX, EAX contained the address
                   of the function that called it.

       b) Repairing the import table

          It's time to repair the import table. In order
          to do this we have to write a simple algorithm
          that replaces the invalid references in the
          import table by the correct addresses of
          the imported functions. Before we can do this
          we have to find out how many Kernel32 and
          User32 imports there are. You have maybe
          noticed that the next referenced routine
          always follows after 25h bytes. Trace into
          one of them and enter the following command:
          (the instruction pointer has to be at the
          PUSHAD operation)

          "u eip+25*x"

          with x being any number you have to constantly
          increase until you reached the last routine.
          In this case there were 5Fh Kernel 32 imports.
          The routines that calculated the addresses of
          the User32 imports were 29h bytes away from
          the last Kernel32 routine. By using the same
          method as above you can find out how many User32
          imports there are (28h in my case).
          You may want to write down these values.
          If you don't want to do this you may also use
          the program "IMPORT.EXE" to find out how many
          imported functions there are. Just start it,
          select the "XXX.ICD" file and click "Get Info".
          The program will then generate a file called
          "IMPORT.TXT" in the directory of the file you
          just selected. This textfile contains information
          about all imported DLLs in the executable.
          Now we can finally repair the import table.
          Start the program and set the instruction pointer
          to the start address of ADump. Then copy the
          file's import table into a memory area that's
          1000h behind the ADump start address:

          "m 735F000 l 46000 82C19000"
             ^         ^     ^
             |         |     start address of ADump + 1000h
             |         size of the .rdata section
             address of .rdata section

          Then assemble the following algorithm:

          01: PUSH        EBX
          02: PUSH        0
          03: CALL        A800C0
          04: ADD         ESP,8
          05: ADD         ECX,EDX
          06: MOV         EDX,735F000
          07: CMP         DWORD PTR [EDX],ECX
          08: JE          EIP+D
          09: INC         EDX
          10: CMP         EDX,735F000 + 46000 - 3
          11: JE          EIP+12
          12: JMP         EIP-D
          13: SUB         EDX,735F000
          14: ADD         EDX,82C19000
          15: MOV         DWORD PTR [EDX],EAX
          16: INC         EBX
          18: CMP         EBX,5F
          19: JBE         82C18000
          20: JMP         EIP

          Explanations:
          01: EBX contains the actual function number and
              has to be manually set to 0 before you run
              it.
          02: DLL number.
          03: This calls the safedisc function to obtain
              the wrong reference and the correct address.
          05: ECX now contains the address of the wrong
              reference. EDX has to be manually set to 0
              before you run it.
          07: The wrong reference is searched in the
              import table.
          10: The search will be aborted if the end
              of the .rdata section is reached (address
              of the section + size - 3).
          13: Calcuation of the address of the wrong
              reference in our copied .rdata section.
          15: This replaces the wrong reference by the
              correct address.
          16: Next function.
          17: Check if all functions have been processed.
          20: Infinite loop.

          Now you may run this small program (after having
          set EBX and EDX to 0). You may set a break point
          on line 20 so you will know when all entries of
          that DLL have been replaced.
          As soon as the break point's triggered you have
          to set EBX and EDX to 0 again and replace the
          PUSH 0 in line 2 by PUSH 1 and the CMP EBX,5F
          in line 18 by CMP EBX,28 to process all User32
          imports. When breakpoints triggered the second
          time you may exit the program by killing the
          process using ProcDump or assembling a call to
          ExitProcess at the current code location.
          The values shown above (address of the .rdata
          section and so on) have to be replaced by
          the ones used in the program you want to crack.
          Note --> It is possible that the safedisc function
                   does no return a valid address (e.g.
                   if you tracest into a call before you
                   assembled the small program). To prevent
                   this you have to do this:
                   Trace into the safedisc function until
                   these operations appear:
                   CMP     EAX,1
                   JNZ     XXXXXXXX
                   Place a break point at the address of
                   the jump:
                   "bpx XXXXXXXX if (eax==1) do "r fl=z;g;"
                   This automatically switches the zero
                   flag if EAX should be 1.
          Now you may dump the repaired .rdata section
          to a file using ADump:

          "W C:\XXX\XXX\RDATA.BIN 46000 82C19000"

          Now we have to replace the .rdata section of
          the dumped file by the one we just wrote to
          disk. Find out at which offset the section
          starts in the file using ProcDump, delete
          it from the file and insert the new one.

       c) Compatibility issues

          At this point you might think that we were
          finished as the program runs like the gentle
          wind. But unfortunately there's one more problem
          we have to face:
          As there are dozens of different versions of
          windows there consequently are dozens of
          Kernel32 and User32 DLLs. These are not always
          located at the same addresses in memory.
          As a consequence the cracked program will
          only run on your operating system.
          To describe my solution to this program I
          habe to go a long way back. So here comes
          a short description of the format of the
          import table:

          As mentioned before the import table is needed
          to call functions of the windows API or other
          DLLs. The DLLs may be located at different
          addresses each time you can only be determine
          during run time thus they cannot be compiled
          into the executable. So all function calls in
          a PE file do not refer to a specific linear
          address but to a location that is part of the
          import table in which the operation system
          writes the correct address of the function
          the program wants to call. The structure
          of the import table is as follows (I'll
          leave out the advanced stuff):

          The import table start with the import
          directory. This directory is an array of
          IMAGE_IMPORT_DESCRIPTORs, one of them for each
          imported DLL. The array is terminated by
          an IMAGE_IMPORT_DESCRIPTOR that is completely
          filled with 0-bytes.
          Each IMAGE_IMPORT_DESCIPTOR is based on the
          following structure:

          OriginalFirstThunk
            An RVA (32 bit) that point to a zero terminated
            array of other RVAs.

          TimeDateStamp
            32 bit timestamp, not interesting at the moment.

          ForwarderChain
            32 bit index of the next forwarder - advanced
            stuff and so it's not interesting for us.

          Name
            A 32 bit RVA to the name of the DLL (which is
            a zero terminated string).

          FirstThunk
            Another 32 bit RVA to another zero terminated
            array of more RVAs.

          So each IMAGE_IMPORT_DESCRIPTOR gives us the
          name of the DLL and two RVAs that point to two
          arrays. These arrays are both zero terminated
          and consist of RVA which (usually) point to
          an IMAGE_IMPORT_BY_NAME. The two arrays run
          parallel, i.e. the point to the same IMAGE_-
          IMPORT_BY_NAMEs.

          The IMAGE_IMPORT_BY_NAMEs consist of a 16 bit
          number (ordinal of the function) and a zero
          terminated string, the function name.

          Now if a program's run the operating system
          searches the array that OriginalFirstThunk
          points to, retrieves the function names and
          writes the correct memory addresses of these
          functions into the array that FirstThunk points
          to. The program can now easily access all the
          functions it needs via this array. It's just
          this array that our simple algorithm already
          filled with the correct addresses.
          So in this case the OS cannot put in the
          correct addresses as futhermore the entry
          OriginalFirstThunk is zero in our dumped
          executable! The function names are removed
          by safedisc, too. So how the hell do we get
          it to work again?
          Some time and about 800 lines of assembly code
          later I found a solution.

          To deal with this problem I wrote two programs:
          - GettAddr
            This programs reads *all* function names
            of the Kernel32 and User32 DLLs out of
            two files (KERNEL32.EXP and USER32.EXP) and
            writes the correct function addresses
            into the files KERNEL32.ADR and USER32.ADR.
          - RepImp
            This program does the big part of the work.
            It reads the addresses that are located
            in the XXX32.ADR files and searches for
            them in the import table of a EXE file you
            can specify. If it finds the current
            address it will add the corresponding
            function name to the end of the file and
            change the array we filled with the correct
            function addresses before so it then points
            to the function name. By doing this the
            operating system can find the function names
            and write the correct addresses into the
            array.
            It's also possible to manually examine the .rdata
            section using a hex editor to find a place to
            insert the function names. The offset of this
            place can be entered in RepImp after you have
            changed from "append" to "insert" mode. Here's
            an example: The .rdata section's raw data in the
            file starts at 001F600h, the place you want the
            function names to be inserted is located at
            001F750h. So you have to enter 150 as offset in
            RepImp.
            Note --> The insert mode should only be used if
                     the exe file doesn't work any more after
                     repairing it using the append mode. Moreover
                     it should only be used by persons who
                     know what they are doing.
          Did you get that? Well, I hope so. To make
          your dumped exe file compatible you just
          have to run GetAddr and process the file using
          RepImp thereafter.
          Note --> RepImp will always increase the filesize
                   by 32 KB when in append mode. You can leave
                   it as it is or remove unnecessary zero padding
                   after the function names.
          Attention: I cannot guarantee that these program
          are 100% bug free. Moreover both of them may
          act strange if there are some files missing,
          so be sure all needed files (included the dumpes
          exe file) are in the directory the programs are
          located in.
          I tested them using "Soulreaver", "Rogue Spear" and
          "GTA 2" and did not experience any problems.
          Note --> These programs should also work well
                   with dumped exe files that were pro-
                   tected by the old safedisc version.
          If you did nothing wrong you should now have a
          completely fixed executable!

    III.Final words / thanks & greetings / contacting the author

        Using this knowledge it should be possible for
        you to crack every safedisc protected program
        in ten minutes (I'm wondering when the next version
        will come out ;).

        Greetz und thanks go to
        - all members of AmoK
        - Black Check for his good Safedisc Tutorial
        - +Frog's Print for FrogSice
        - G-RoM, Lorian and Stone for ProcDump
        - tHeRaiN for ADump
        - Numega for SoftIce
        - C-Dilla for Safedisc
        - All I have forgotten

        Send your praises and criticism to:
        tola@gmx.at

        Visit AmoK:
        travel.to/amok
        amok.notrix.de
        amok.mtv.to
